package org.dht.com.tra;

import java.util.concurrent.Semaphore;

import org.apache.log4j.Logger;

/**
 * The {@code WaitingResult} class is responsible for waiting for a response to
 * a message with a specific sequence number. When a response for a given
 * message does not arrive in a specific amount of time this class return
 * {@code null} as the response for that message
 */
public class WaitingResult<T> extends Thread {
	/**
	 * Logger
	 */
	private static final Logger logger = Logger.getLogger(WaitingResult.class);

	/**
	 * The sequence number of the message
	 */
	private long sequence;

	/**
	 * The response for the message with the specific sequence number
	 */
	private T result;

	/**
	 * This semaphore is blocked when asked for the response to a certain
	 * message, and is released when the response to the message arrives
	 */
	private Semaphore semaphore;

	/**
	 * This variable is used for Knowing if a response for a message has arrived
	 */
	private boolean stop;

	/**
	 * This variable is the specific amount of time that the class waits for a
	 * response of a given message
	 */
	private long timeOut = -1;

	/**
	 * Reference to a ReturnsManager instance
	 */
	private ReturnsManager<T> returnsManager;

	/**
	 * The constructor of the class. Constructs a WaitingResult instance that
	 * wait for the response of the message with the sequence number specified
	 * 
	 * @param sequence
	 *            . The sequence number of the message
	 * @param returnsManagerCommunication
	 */
	WaitingResult(long sequence, ReturnsManager<T> returnsManager) {
		this.sequence = sequence;
		this.returnsManager = returnsManager;
		this.stop = false;
		this.semaphore = new Semaphore(0);
	}

	/**
	 * The constructor of the class. Constructs a WaitingResult instance that
	 * wait for the response of the message with the sequence number specified
	 * 
	 * @param sequence
	 *            . The sequence number of the message
	 * @param returnsManagerCommunication
	 */
	WaitingResult(long sequence, ReturnsManager<T> returnsManager, long timeOut) {
		this(sequence, returnsManager);
		this.timeOut = timeOut;
	}

	/**
	 * Executes the thread while <code>stop==false</code> and
	 * <code>timeOut!=0</code>. This is, while the response for the message has
	 * not arrived or while the specific amount of time has not finished. If
	 * timeOut is -1, waiting until that the response arribe
	 */
	public void run() {

		if (timeOut != -1) {

			while (!stop && timeOut != 0) {
				try {
					sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				timeOut--;
			}

		} else {

			while (!stop) {
				try {
					sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}

		if (!stop) {

			logger.debug("Timeout waiting for a response for number sequence= '"
					+ sequence + "'");

			logger.debug("Timeout waiting for a response");

			returnsManager.releaseWaitingResult(sequence, null);
		}
	}

	/**
	 * This method is used for getting the response of the message
	 * 
	 * @return Returns the response for the message
	 */
	public T getResult() {
		start();
		try {
			semaphore.acquire();
		} catch (InterruptedException e) {
			logger.error("Error to stop semaphore", e);
		}

		return result;
	}

	/**
	 * This method is used for setting the response of the message
	 * 
	 * @param result
	 *            . This is the response of the message
	 */
	public void setResult(T result) {
		this.result = result;

		semaphore.release();

		stop = true;
	}

}
